Introduction

Part of the Accelerator Readiness Review is looking at muons that leave the ring. To this end, we (Renee, Nathan, James, and myself) have a preliminary version of MDC-2 with full injection, unified fields, and “ghost” detectors in a Geant parallel world (so these sensitive volumes can be placed without overlapping physical structures). We can remove various magnets and fields and see how that affects muons escaping the beam.

# Load libraries
library(readGallery)
library(stringr)
library(dplyr)
library(ggplot2)
library(rgl)
library(readr)
library(purrr)
library(parallel)

The useDataProduct calls should all come together too.

useDataProduct('std::vector<gm2truth::TrackingActionArtRecord>')
useDataProduct('std::vector<gm2truth::RingTrackingPlaneArtRecord>')
useDataProduct('std::vector<gm2truth::GhostDetectorArtRecord>')

Samples

I have run preliminary MDC-2 (using Renee’s FCL files), making 10K events for various scenarios. Files are currently stored in /pnfs/GM2/scratch/users/lyon/arr_20170313 and directories within. Ghost detectors included are Renee’s cylinder just at the outer vacuum wall and encompossing the inflector as well as my ghost detector that is a cylindrical shell just on the inside of the world cube. Note that this code includes the magnet yoke steel.

Find the data

Need a function to turn a /pnfs path into a xrootd url

xrootdify <- function(p) {
  # We /pnfs/BLA --> root://fndca1.fnal.gov/pnfs/fnal.gov/usr/BLA
  paste0('root://fndca1.fnal.gov/pnfs/fnal.gov/usr', str_replace(p, '/pnfs', ''))
}
# Let's look at only the 180* job series (so we just get 10,000 events per scenario)
system("ssh lyon@gm2gpvm04.fnal.gov 'ls /pnfs/GM2/scratch/users/lyon/arr_20170313/180*/*.root'", intern=T) %>% xrootdify() -> arrFiles
arrFiles
[1] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root"   
[2] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089095_0/ARR_unified_noDipole_cyl.root"     
[3] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089097_0/ARR_unified_noInflector_cyl.root"  
[4] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089099_0/ARR_unified_noKickerQuads_cyl.root"
[5] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089123_0/ARR_unified_noKicker_cyl.root"     
[6] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089132_0/ARR_unified_noQuads_cyl.root"      

Let’s pluck out the scenario.

scenarios <- str_match(arrFiles, 'arr_20170313/.+_unified_(.+)_cyl')[,2]
scenarios
[1] "everything"    "noDipole"      "noInflector"   "noKickerQuads" "noKicker"      "noQuads"      

What data are in these file? Well, product_sizes_dumper doesn’t seem to work across XRootD. That’s too bad.

Here’s a picture from Root.

Look at Tracking action data to see how many muons decay and how many escape

Create a reader class (modify outside of this notebook) for TrackingActionArtRecord

readerClassSkel('gm2truth::TrackingActionArtRecord', writeFile = 'trackingActionReader.py')

trackingActionReader

For documentation, here is the TrackingActionArtRecordReader reader class

readr::read_file('trackingActionReader.py') %>% cat
from readGallery import GalleryReaderBase  # Necessary for the base class

class TrackingActionArtRecordReader(GalleryReaderBase):
  def __init__(self, inputTag, evLimit):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'trackType', 'trackID', 
                  'parentTrackID', 'volumeUID', 'status', 'x', 'y', 'z', 'px', 'py', 'pz']
    self.evLimit = evLimit

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.TrackingActionArtRecord))

  def fill(self, ROOT, ev):
    
    if ev.eventEntry() > self.evLimit:
      return False
    
    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2truth::TrackingActionArtRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2truth::TrackingActionArtRecord
      for e in p:                             # Loop over elements and fill
      
        # Let's only accept the muon
        if e.trackID != 1 or e.parentTrackID != 0:
          continue

        self.vals.append(
          [ ev.fileEntry(), ev.eventEntry(), e.trackType, e.trackID, e.parentTrackID,  e.volumeUID, 
            e.status, e.x, e.y, e.z, e.px, e.py, e.pz ])

    return True

Make an instance

trackingActionReaderClass <- createReaderClass_from_file('trackingActionReader.py')$TrackingActionArtRecordReader

Load the data - let’s just look at one file since it takes a long time

taReader <- trackingActionReaderClass(artInputTag("artg4"), 20000)
getGalleryData(arrFiles[1], taReader)
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root
Closing file, read 696635144 bytes in 163 transactions
Timings: Overall time = 106.982 s
Time per event: min=0.000   mean=0.009   max=6.802

As of right now, the dCache XRootD door is down - no - it’s back up

taDF <- galleryReader_df(taReader)
nrow(taDF)
[1] 20000

Set the scenario (we’ll need that again, so make it a function)

setScenario <- function(df) {
  df %>% mutate(scenario = factor(fileEntry, levels=0:(length(scenarios)-1), labels=scenarios))
}
taDF %>% setScenario() %>% select(scenario, everything()) -> taDF
taDF

How many track action hits do we get per scenario.

taDF %>% group_by(scenario) %>% tally()

Let’s just get the death of the muons.

Muon deaths in tracking action

taDF %>% filter(status == 1) -> taDeathDF

We need the volume ID. I have a art FCL to run to get this information. I have files and they each may have a different set of volume IDs. Let’s try to do this in parallel. The examples in the help for parallel::mcparallel seem to be useful here.

Create the command strings,

runForVolIDString <- function(i) {
  str_interp(
    "PVS_CSVOUT=${csvout}_volNames.csv gm2 -c ${fclPath}/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 ${aFile}",
    list( csvout=scenarios[i], fclPath=Sys.getenv("MRB_BUILDDIR"), aFile=arrFiles[i]) 
  )
}
runForVolIDStrings <- sapply(1:length(arrFiles), runForVolIDString)
runForVolIDStrings
[1] "PVS_CSVOUT=everything_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root"      
[2] "PVS_CSVOUT=noDipole_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089095_0/ARR_unified_noDipole_cyl.root"          
[3] "PVS_CSVOUT=noInflector_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089097_0/ARR_unified_noInflector_cyl.root"    
[4] "PVS_CSVOUT=noKickerQuads_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089099_0/ARR_unified_noKickerQuads_cyl.root"
[5] "PVS_CSVOUT=noKicker_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089123_0/ARR_unified_noKicker_cyl.root"          
[6] "PVS_CSVOUT=noQuads_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089132_0/ARR_unified_noQuads_cyl.root"            

Let’s run in parallel

jobs <- lapply(1:length(arrFiles), function(i) mcparallel(system( runForVolIDStrings[i], intern=T ), name=i))
res <- mccollect(jobs)

Now we need to load the csv files. Let’s do that in parallel too

jobs <- lapply(1:length(arrFiles), function(i) paste0(scenarios[i], "_volNames.csv") %>% read_csv(col_names=c("volumeUID", "volName")) %>% mcparallel(name=i) )
volNameTables <- mccollect(jobs)
names(volNameTables) <- scenarios

Let’s see what we got

volNameTables[["everything"]]

Join them together when the scenario name.

volNameTable <- map2_df(volNameTables, scenarios, function(df, sce) df %>% mutate(scenario = sce) )
volNameTable

Now let’s do lookups… [Note that I can’t do what I originally thought, which was split up the data frame into groups by scenario and then put in the factor by group – because in the end the rows get rejoined and then row-binded factor gets messed up.]

taDeathDF -> hold # in case we mess up
taDeathDF %>% inner_join(volNameTable) %>% select(scenario, eventEntry, volName, everything()) -> taDeathDF
Joining, by = c("scenario", "volName", "volumeUID")
taDeathDF

We can check this – we should see different volume names associated with volume IDs for different scenarios

taDeathDF %>% distinct(scenario, volumeUID, volName) %>% arrange(volumeUID)

Let’s plot where things die

taDeathDF %>% group_by(scenario, volName) %>% tally() %>% arrange(scenario, desc(n)) -> deathVolumeTable
deathVolumeTable

Did any of them actually decay?

taDeathDF %>% filter(volName == "ArcSection[00]")

Uh oh!!!! Hardly any decay!

taDeathDF %>% ggplot( aes(x = volName) ) + geom_bar() + theme(axis.text.x = element_text(angle=90, hjust=1))

Look at why hardly any muons decay.

Let’s look at the ‘everything’ scenario.

everythingFile <- which(scenarios == "everything")

I made a new TrackingAction reader

readr::read_file('trackingActionReaderCheckDecay.py') %>% cat
# Store the muon at death and whatever it decayed to at birth

from readGallery import GalleryReaderBase  # Necessary for the base class

class TrackingActionWithDecayReader(GalleryReaderBase):
  def __init__(self, inputTag, evLimit):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'trackType', 'trackID', 
                  'parentTrackID', 'volumeUID', 'status', 'x', 'y', 'z', 'px', 'py', 'pz', 'e', 'turn']
    self.evLimit = evLimit

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.TrackingActionArtRecord))

  def fill(self, ROOT, ev):
    
    if ev.eventEntry() > self.evLimit:
      return False
    
    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2truth::TrackingActionArtRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2truth::TrackingActionArtRecord
      for e in p:                             # Loop over elements and fill
      
        # Let's only accept the muon at death or whatever the muon decays to at birth
        if (e.trackID == 1) or (e.parentTrackID == 1 and e.status == 0):

          self.vals.append(
            [ ev.fileEntry(), ev.eventEntry(), e.trackType, e.trackID, e.parentTrackID,  e.volumeUID, 
              e.status, e.x, e.y, e.z, e.px, e.py, e.pz, e.e, e.turn ])

    return True

Make an instance

taDecayClass <- createReaderClass_from_file('trackingActionReaderCheckDecay.py')$TrackingActionWithDecayReader

Load the data - let’s just look at one file since it takes a long time

taReader <- taDecayClass(artInputTag("artg4"), 20000)
getGalleryData(arrFiles[everythingFile], taReader)
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root
tadDF <- galleryReader_df(taReader)
nrow(tadDF)
[1] 52842

Make things easier to look at

tadDF %>% 
  mutate(r    = sqrt(x*x+y*y),
         pmag = sqrt(px*px + py*py + pz*pz) ) %>% 
  select(trackType, volumeUID, status, r, e, pmag, trackID, turn, x, y, z) -> tadDF

So I see a positron in alomst every case. But the muon appears to stop in the iron. Let’s just plot x,z

taDeathDF %>% ggplot(aes(x=z, y=x)) + 
  geom_point() + xlim(c(-10000, 10000))

tmp <- open3d()
ring <- cylinder3d( center=rbind(c(0,-90, 0), c(0,90,0)),
                    radius=7112,
                    sides=20, closed = F)
clear3d()
with(taDeathDF, 
     plot3d(x=x, y=y, z=z, type='p', main='Muon death positions', 
       xlab="x", ylab="y", zlab="z", xlim=c(-9000, 9000), zlim=c(-9000, 9000)))
plot3d(ring, add=T, alpha=0.2)
view3d(phi=90, theta=-90)
rglwidget()

This really doesn’t look like muons are being stored.

What fraction of muons have zero momentum when they die (so they die in something dense),

tadDF %>% 
  filter( trackID == 1, status == 1) %>% 
  summarize(pZeroPct = sum(pmag<0.1)/nrow(.)*100) -> pZeroPct
pZeroPct

So % of muons die in something dense. How many decay?

tadDF %>% filter(status == 1, trackID == 1, pmag>0.1)  -> tad.muonsWithP.DF
tad.muonsWithP.DF
clear3d()
with(tad.muonsWithP.DF, 
     plot3d(x=x, y=y, z=z, type='p', main='Muon death with momentum positions', 
       xlab="x", ylab="y", zlab="z", xlim=c(-9000, 9000), zlim=c(-9000, 9000)))
plot3d(ring, add=T, alpha=0.2)
view3d(phi=90, theta=-90)
rglwidget()

These really don’t look stored.

Ring tracking planes

Look at the RingTrackingPlaneArtRecord.

readerClassSkel(extractClass = "gm2truth::RingTrackingPlaneArtRecord", fillClass = "gm2truth::BasicArtRecord", 
                writeFile = 'ringTrackingPlaneReader.py')
readr::read_file('ringTrackingPlaneReader.py') %>% cat
from readGallery import GalleryReaderBase  # Necessary for the base class

class RingTrackingPlaneArtRecordReader(GalleryReaderBase):
  def __init__(self, inputTag):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'particle',
                  'trkid', 'parentid', 'pdgid', 'nturn', 'fracturn', 'detname', 
                  'detnum', 'time', 'energy', 'x', 'y', 'z', 'px', 'py', 'pz',
                  'radius', 'angle']
    # !!!! Modify the names accordingly - MUST MATCH self.vals.append CALL BELOW

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.RingTrackingPlaneArtRecord))

  def fill(self, ROOT, ev):

    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2truth::RingTrackingPlaneArtRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2truth::BasicArtRecord
      for e in p:                             # Loop over elements and fill
        
        f = e.basicInfo

        self.vals.append(
          [ ev.fileEntry(), ev.eventEntry(), f.particle, f.trkid, f.parentid, f.pdgid, f.nturn, e.fturn, f.detname,
            f.detnum, f.time, f.energy, f.pos.x(), f.pos.y(), f.pos.z(), 
            f.mom.x(), f.mom.y(), f.mom.z(), f.radius, f.angle ])

    return True
rtpC <- createReaderClass_from_file('ringTrackingPlaneReader.py')$RingTrackingPlaneArtRecordReader

Make one and load the data

rtpR <- rtpC(artInputTag('artg4:RingTrackingPlanes'))
getGalleryData(arrFiles[everythingFile], rtpR) -> timings
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root

How many events did we see

length(timings$eventTimes)
[1] 10000
rtpDF <- galleryReader_df(rtpR)
nrow(rtpDF)
[1] 4

Only 4 events had a ring tracking plane hit?

rtpDF

Nathan fixes it

Nathan pushed some now code that supposedly fixes this. Let’s look.

Ring tracking planes

myNewFile <- '/home/me/gm2/renee_arr/try/mdc2-1/ARR_unified_everything_cyl.root'
getGalleryData(myNewFile, rtpR) -> timings
Opening first file...
Successfully opened file /home/me/gm2/renee_arr/try/mdc2-1/ARR_unified_everything_cyl.root
Closing file, read 765054 bytes in 57 transactions
Timings: Overall time = 0.130 s
Time per event: min=0.000   mean=0.000   max=0.016
length(timings$eventTimes)
[1] 1000
newRtpDF <- galleryReader_df(rtpR)
nrow(newRtpDF)
[1] 3203
newRtpDF

Now I see a lot more muons going around the ring!!

How many turns?

newRtpDF %>% ggplot(aes(x=nturn)) + geom_histogram(bins=100)

Not sure if this is right, but it looks better than before!

Let’s look at the volume names and see how mnay of these muons decay. It should be around 10% or so.

Tracking action and volume ids

Let’s load the tracking action data and the volume name.

First, load the tracking action data…

tanReader <- trackingActionReaderClass(artInputTag("artg4"), 20000)
getGalleryData(myNewFile, tanReader)
Opening first file...
Successfully opened file /home/me/gm2/renee_arr/try/mdc2-1/ARR_unified_everything_cyl.root
Closing file, read Timings: Overall time = 2.840 s
66328385 bytes in 597 transactions
Time per event: min=0.000   mean=0.003   max=0.685
tanDF <- galleryReader_df(tanReader)
tanDF

Just look at the muon death

tanDF %>% filter(trackID == 1, status == 1) -> tanDeathDF
tanDeathDF

Put in volume name

runVolString <- str_interp("PVS_CSVOUT=${csvout}_volNames.csv gm2 -c ${fclPath}/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 ${aFile}",
    list( csvout='nathanFixed', fclPath=Sys.getenv("MRB_BUILDDIR"), aFile=myNewFile) )
runVolString
[1] "PVS_CSVOUT=nathanFixed_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 /home/me/gm2/renee_arr/try/mdc2-1/ARR_unified_everything_cyl.root"
system(runVolString)
%MSG-i MF_INIT_OK:  gm2 21-Mar-2017 14:31:10 UTC JobSetup
Messagelogger initialization complete.
%MSG
21-Mar-2017 14:31:11 UTC  Initiating request to open input file "/home/me/gm2/renee_arr/try/mdc2-1/ARR_unified_everything_cyl.root"
21-Mar-2017 14:31:12 UTC  Opened input file "/home/me/gm2/renee_arr/try/mdc2-1/ARR_unified_everything_cyl.root"
Begin processing the 1st record. run: 1 subRun: 0 event: 1 at 21-Mar-2017 14:31:12 UTC
21-Mar-2017 14:31:12 UTC  Closed input file "/home/me/gm2/renee_arr/try/mdc2-1/ARR_unified_everything_cyl.root"

TrigReport ---------- Event  Summary ------------
TrigReport Events total = 1 passed = 1 failed = 0

TrigReport ------ Modules in End-Path: end_path ------------
TrigReport  Trig Bit#    Visited     Passed     Failed      Error Name
TrigReport     0    0          1          1          0          0 pvsToFile

TimeReport ---------- Time  Summary ---[sec]----
TimeReport CPU = 0.000000 Real = 0.000137

Art has completed and will exit with status 0.
volNames <- read_csv('nathanFixed_volNames.csv', col_names=c("volumeUID", "volName"))
Parsed with column specification:
cols(
  volumeUID = col_integer(),
  volName = col_character()
)
volNames

Let’s join!

tanDeathDF %>% inner_join(volNames) %>% select(eventEntry, volName, everything()) -> tanDeathDF
Joining, by = "volumeUID"
tanDeathDF
tanDeathDF %>% distinct(volumeUID, volName) %>% arrange(volumeUID)
tanDeathDF %>% group_by(volName) %>% tally() %>% arrange(desc(n))

So, about 1% decay!

tanDeathDF %>% 
  mutate(r    = sqrt(x*x+z*z),
         pmag = sqrt(px*px + py*py + pz*pz) ) %>% 
  select(eventEntry, trackType, volName, status, r, pmag, trackID, x, y, z) -> tanDeathdDF
tanDeathdDF %>% filter(volName == 'ArcSection[00]')

Do these correspond to the muons that make many turns? We need to do a join with the ring tracking planes

tanDeathdDF %>% inner_join(newRtpDF, by="eventEntry", suffix=c(".trk", "rtp")) -> tanDeathRTPDF
tanDeathRTPDF

Let’s look at guys that have more than one turn. Remember, we get a row for every hit. If we really want to see how many turns we get, we need to look at the last hit for the event.

tanDeathRTPDF %>% filter(fracturn >= 1) %>% arrange(eventEntry, fracturn) %>% group_by(eventEntry) %>% do(tail(., 1)) %>%
  select(eventEntry, volName, r, pmag, fracturn )

Hmm - we’re missing some muons. Let’s look at those.

tanDeathRTPDF %>% filter(volName == 'ArcSection[00]') %>% arrange(eventEntry, fracturn) %>% group_by(eventEntry) %>% do(tail(., 1)) %>%
  select(eventEntry, volName, r, pmag, fracturn )

What happened to event 791?

newRtpDF %>% filter(eventEntry == 791)

Let’s look at some muon tracks. How about 791 and 893. Since our file is on disk, this will be faster.

LS0tCnRpdGxlOiAiRXNjYXBlZCBNdW9ucyIKYXV0aG9yOiAiQWRhbSBMeW9uIgpkYXRlOiAyMDE3LTAzLTEwCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGU9VFJVRSwgYXV0b2RlcD1UUlVFKQpgYGAKCiMgSW50cm9kdWN0aW9uClBhcnQgb2YgdGhlIEFjY2VsZXJhdG9yIFJlYWRpbmVzcyBSZXZpZXcgaXMgbG9va2luZyBhdCBtdW9ucyB0aGF0IGxlYXZlIHRoZSByaW5nLiBUbyB0aGlzIGVuZCwgd2UgKFJlbmVlLCBOYXRoYW4sIEphbWVzLCBhbmQgbXlzZWxmKSBoYXZlIGEgcHJlbGltaW5hcnkgdmVyc2lvbiBvZiBNREMtMiB3aXRoIGZ1bGwgaW5qZWN0aW9uLCB1bmlmaWVkIGZpZWxkcywgYW5kICJnaG9zdCIgZGV0ZWN0b3JzIGluIGEgR2VhbnQgcGFyYWxsZWwgd29ybGQgKHNvIHRoZXNlIHNlbnNpdGl2ZSB2b2x1bWVzIGNhbiBiZSBwbGFjZWQgd2l0aG91dCBvdmVybGFwcGluZyBwaHlzaWNhbCBzdHJ1Y3R1cmVzKS4gV2UgY2FuIHJlbW92ZSB2YXJpb3VzIG1hZ25ldHMgYW5kIGZpZWxkcyBhbmQgc2VlIGhvdyB0aGF0IGFmZmVjdHMgbXVvbnMgZXNjYXBpbmcgdGhlIGJlYW0uIAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShyZWFkR2FsbGVyeSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmdsKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KHBhcmFsbGVsKQpgYGAKClRoZSBgdXNlRGF0YVByb2R1Y3RgIGNhbGxzIHNob3VsZCBhbGwgY29tZSB0b2dldGhlciB0b28uIApgYGB7cn0KdXNlRGF0YVByb2R1Y3QoJ3N0ZDo6dmVjdG9yPGdtMnRydXRoOjpUcmFja2luZ0FjdGlvbkFydFJlY29yZD4nKQp1c2VEYXRhUHJvZHVjdCgnc3RkOjp2ZWN0b3I8Z20ydHJ1dGg6OlJpbmdUcmFja2luZ1BsYW5lQXJ0UmVjb3JkPicpCnVzZURhdGFQcm9kdWN0KCdzdGQ6OnZlY3RvcjxnbTJ0cnV0aDo6R2hvc3REZXRlY3RvckFydFJlY29yZD4nKQpgYGAKCiMgU2FtcGxlcwpJIGhhdmUgcnVuIHByZWxpbWluYXJ5IE1EQy0yICh1c2luZyBSZW5lZSdzIEZDTCBmaWxlcyksIG1ha2luZyAxMEsgZXZlbnRzIGZvciB2YXJpb3VzIHNjZW5hcmlvcy4gRmlsZXMgYXJlIGN1cnJlbnRseSBzdG9yZWQgaW4gYC9wbmZzL0dNMi9zY3JhdGNoL3VzZXJzL2x5b24vYXJyXzIwMTcwMzEzYCBhbmQgZGlyZWN0b3JpZXMgd2l0aGluLiBHaG9zdCBkZXRlY3RvcnMgaW5jbHVkZWQgYXJlIFJlbmVlJ3MgY3lsaW5kZXIganVzdCBhdCB0aGUgb3V0ZXIgdmFjdXVtIHdhbGwgYW5kIGVuY29tcG9zc2luZyB0aGUgaW5mbGVjdG9yIGFzIHdlbGwgYXMgbXkgZ2hvc3QgZGV0ZWN0b3IgdGhhdCBpcyBhIGN5bGluZHJpY2FsIHNoZWxsIGp1c3Qgb24gdGhlIGluc2lkZSBvZiB0aGUgd29ybGQgY3ViZS4gTm90ZSB0aGF0IHRoaXMgY29kZSBpbmNsdWRlcyB0aGUgbWFnbmV0IHlva2Ugc3RlZWwuIAoKIyMgRmluZCB0aGUgZGF0YQoKTmVlZCBhIGZ1bmN0aW9uIHRvIHR1cm4gYSBgL3BuZnNgIHBhdGggaW50byBhIHhyb290ZCB1cmwKCmBgYHtyfQp4cm9vdGRpZnkgPC0gZnVuY3Rpb24ocCkgewogICMgV2UgL3BuZnMvQkxBIC0tPiByb290Oi8vZm5kY2ExLmZuYWwuZ292L3BuZnMvZm5hbC5nb3YvdXNyL0JMQQogIHBhc3RlMCgncm9vdDovL2ZuZGNhMS5mbmFsLmdvdi9wbmZzL2ZuYWwuZ292L3VzcicsIHN0cl9yZXBsYWNlKHAsICcvcG5mcycsICcnKSkKfQpgYGAKCgpgYGB7cn0KIyBMZXQncyBsb29rIGF0IG9ubHkgdGhlIDE4MCogam9iIHNlcmllcyAoc28gd2UganVzdCBnZXQgMTAsMDAwIGV2ZW50cyBwZXIgc2NlbmFyaW8pCnN5c3RlbSgic3NoIGx5b25AZ20yZ3B2bTA0LmZuYWwuZ292ICdscyAvcG5mcy9HTTIvc2NyYXRjaC91c2Vycy9seW9uL2Fycl8yMDE3MDMxMy8xODAqLyoucm9vdCciLCBpbnRlcm49VCkgJT4lIHhyb290ZGlmeSgpIC0+IGFyckZpbGVzCmFyckZpbGVzCmBgYApMZXQncyBwbHVjayBvdXQgdGhlIHNjZW5hcmlvLgpgYGB7cn0Kc2NlbmFyaW9zIDwtIHN0cl9tYXRjaChhcnJGaWxlcywgJ2Fycl8yMDE3MDMxMy8uK191bmlmaWVkXyguKylfY3lsJylbLDJdCnNjZW5hcmlvcwpgYGAKCldoYXQgZGF0YSBhcmUgaW4gdGhlc2UgZmlsZT8gV2VsbCwgYHByb2R1Y3Rfc2l6ZXNfZHVtcGVyYCBkb2Vzbid0IHNlZW0gdG8gd29yayBhY3Jvc3MgWFJvb3RELiBUaGF0J3MgdG9vIGJhZC4gCgpIZXJlJ3MgYSBwaWN0dXJlIGZyb20gUm9vdC4KCiFbXShlc2NhcGVkTXVvbnNEYXRhLnBuZykKCiMjIExvb2sgYXQgVHJhY2tpbmcgYWN0aW9uIGRhdGEgdG8gc2VlIGhvdyBtYW55IG11b25zIGRlY2F5IGFuZCBob3cgbWFueSBlc2NhcGUKCkNyZWF0ZSBhIHJlYWRlciBjbGFzcyAobW9kaWZ5IG91dHNpZGUgb2YgdGhpcyBub3RlYm9vaykgZm9yIFRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkCmBgYHIKcmVhZGVyQ2xhc3NTa2VsKCdnbTJ0cnV0aDo6VHJhY2tpbmdBY3Rpb25BcnRSZWNvcmQnLCB3cml0ZUZpbGUgPSAndHJhY2tpbmdBY3Rpb25SZWFkZXIucHknKQpgYGAKCiMjIyBgdHJhY2tpbmdBY3Rpb25SZWFkZXJgCgpGb3IgZG9jdW1lbnRhdGlvbiwgaGVyZSBpcyB0aGUgYFRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkUmVhZGVyYCByZWFkZXIgY2xhc3MKYGBge3J9CnJlYWRyOjpyZWFkX2ZpbGUoJ3RyYWNraW5nQWN0aW9uUmVhZGVyLnB5JykgJT4lIGNhdApgYGAKCk1ha2UgYW4gaW5zdGFuY2UKYGBge3J9CnRyYWNraW5nQWN0aW9uUmVhZGVyQ2xhc3MgPC0gY3JlYXRlUmVhZGVyQ2xhc3NfZnJvbV9maWxlKCd0cmFja2luZ0FjdGlvblJlYWRlci5weScpJFRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkUmVhZGVyCmBgYAoKTG9hZCB0aGUgZGF0YSAtIGxldCdzIGp1c3QgbG9vayBhdCBvbmUgZmlsZSBzaW5jZSBpdCB0YWtlcyBhIGxvbmcgdGltZQpgYGB7cn0KdGFSZWFkZXIgPC0gdHJhY2tpbmdBY3Rpb25SZWFkZXJDbGFzcyhhcnRJbnB1dFRhZygiYXJ0ZzQiKSwgMjAwMDApCmdldEdhbGxlcnlEYXRhKGFyckZpbGVzWzFdLCB0YVJlYWRlcikKYGBgCkFzIG9mIHJpZ2h0IG5vdywgdGhlIGRDYWNoZSBYUm9vdEQgZG9vciBpcyBkb3duIC0gbm8gLSBpdCdzIGJhY2sgdXAgCmBgYHtyfQp0YURGIDwtIGdhbGxlcnlSZWFkZXJfZGYodGFSZWFkZXIpCm5yb3codGFERikKYGBgClNldCB0aGUgc2NlbmFyaW8gKHdlJ2xsIG5lZWQgdGhhdCBhZ2Fpbiwgc28gbWFrZSBpdCBhIGZ1bmN0aW9uKQpgYGB7cn0Kc2V0U2NlbmFyaW8gPC0gZnVuY3Rpb24oZGYpIHsKICBkZiAlPiUgbXV0YXRlKHNjZW5hcmlvID0gZmFjdG9yKGZpbGVFbnRyeSwgbGV2ZWxzPTA6KGxlbmd0aChzY2VuYXJpb3MpLTEpLCBsYWJlbHM9c2NlbmFyaW9zKSkKfQpgYGAKCmBgYHtyfQp0YURGICU+JSBzZXRTY2VuYXJpbygpICU+JSBzZWxlY3Qoc2NlbmFyaW8sIGV2ZXJ5dGhpbmcoKSkgLT4gdGFERgp0YURGCmBgYAoKSG93IG1hbnkgdHJhY2sgYWN0aW9uIGhpdHMgZG8gd2UgZ2V0IHBlciBzY2VuYXJpby4KYGBge3J9CnRhREYgJT4lIGdyb3VwX2J5KHNjZW5hcmlvKSAlPiUgdGFsbHkoKQpgYGAKCkxldCdzIGp1c3QgZ2V0IHRoZSBkZWF0aCBvZiB0aGUgbXVvbnMuIAoKIyMjIE11b24gZGVhdGhzIGluIHRyYWNraW5nIGFjdGlvbgoKYGBge3J9CnRhREYgJT4lIGZpbHRlcihzdGF0dXMgPT0gMSkgLT4gdGFEZWF0aERGCmBgYAoKV2UgbmVlZCB0aGUgdm9sdW1lIElELiBJIGhhdmUgYSBhcnQgRkNMIHRvIHJ1biB0byBnZXQgdGhpcyBpbmZvcm1hdGlvbi4gSSBoYXZlIGByIGNhdChsZW5ndGgoYXJyRmlsZXMpKWAgZmlsZXMgYW5kIHRoZXkgZWFjaCBtYXkgaGF2ZSBhIGRpZmZlcmVudCBzZXQgb2Ygdm9sdW1lIElEcy4gTGV0J3MgdHJ5IHRvIGRvIHRoaXMgaW4gcGFyYWxsZWwuIFRoZSBleGFtcGxlcyBpbiB0aGUgaGVscCBmb3IgYHBhcmFsbGVsOjptY3BhcmFsbGVsYCBzZWVtIHRvIGJlIHVzZWZ1bCBoZXJlLiAKCkNyZWF0ZSB0aGUgY29tbWFuZCBzdHJpbmdzLApgYGB7cn0KcnVuRm9yVm9sSURTdHJpbmcgPC0gZnVuY3Rpb24oaSkgewogIHN0cl9pbnRlcnAoCiAgICAiUFZTX0NTVk9VVD0ke2Nzdm91dH1fdm9sTmFtZXMuY3N2IGdtMiAtYyAke2ZjbFBhdGh9L2dtMmFuYWx5c2VzL2ZjbC9waHlzaWNhbFZvbHVtZVN0b3JlVG9GaWxlLmZjbCAtbiAxICR7YUZpbGV9IiwKICAgIGxpc3QoIGNzdm91dD1zY2VuYXJpb3NbaV0sIGZjbFBhdGg9U3lzLmdldGVudigiTVJCX0JVSUxERElSIiksIGFGaWxlPWFyckZpbGVzW2ldKSAKICApCn0KCnJ1bkZvclZvbElEU3RyaW5ncyA8LSBzYXBwbHkoMTpsZW5ndGgoYXJyRmlsZXMpLCBydW5Gb3JWb2xJRFN0cmluZykKcnVuRm9yVm9sSURTdHJpbmdzCmBgYAoKTGV0J3MgcnVuIGluIHBhcmFsbGVsCmBgYHtyfQpqb2JzIDwtIGxhcHBseSgxOmxlbmd0aChhcnJGaWxlcyksIGZ1bmN0aW9uKGkpIG1jcGFyYWxsZWwoc3lzdGVtKCBydW5Gb3JWb2xJRFN0cmluZ3NbaV0sIGludGVybj1UICksIG5hbWU9aSkpCnJlcyA8LSBtY2NvbGxlY3Qoam9icykKYGBgCgpOb3cgd2UgbmVlZCB0byBsb2FkIHRoZSBjc3YgZmlsZXMuIExldCdzIGRvIHRoYXQgaW4gcGFyYWxsZWwgdG9vCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpqb2JzIDwtIGxhcHBseSgxOmxlbmd0aChhcnJGaWxlcyksIGZ1bmN0aW9uKGkpIHBhc3RlMChzY2VuYXJpb3NbaV0sICJfdm9sTmFtZXMuY3N2IikgJT4lIHJlYWRfY3N2KGNvbF9uYW1lcz1jKCJ2b2x1bWVVSUQiLCAidm9sTmFtZSIpKSAlPiUgbWNwYXJhbGxlbChuYW1lPWkpICkKdm9sTmFtZVRhYmxlcyA8LSBtY2NvbGxlY3Qoam9icykKbmFtZXModm9sTmFtZVRhYmxlcykgPC0gc2NlbmFyaW9zCmBgYAoKTGV0J3Mgc2VlIHdoYXQgd2UgZ290CmBgYHtyfQp2b2xOYW1lVGFibGVzW1siZXZlcnl0aGluZyJdXQpgYGAKSm9pbiB0aGVtIHRvZ2V0aGVyIHdoZW4gdGhlIHNjZW5hcmlvIG5hbWUuCmBgYHtyfQp2b2xOYW1lVGFibGUgPC0gbWFwMl9kZih2b2xOYW1lVGFibGVzLCBzY2VuYXJpb3MsIGZ1bmN0aW9uKGRmLCBzY2UpIGRmICU+JSBtdXRhdGUoc2NlbmFyaW8gPSBzY2UpICkKdm9sTmFtZVRhYmxlCmBgYAoKTm93IGxldCdzIGRvIGxvb2t1cHMuLi4gW05vdGUgdGhhdCBJIGNhbid0IGRvIHdoYXQgSSBvcmlnaW5hbGx5IHRob3VnaHQsIHdoaWNoIHdhcyBzcGxpdCB1cCB0aGUgZGF0YSBmcmFtZSBpbnRvIGdyb3VwcyBieSBzY2VuYXJpbyBhbmQgdGhlbiBwdXQgaW4gdGhlIGZhY3RvciBieSBncm91cCAtLSBiZWNhdXNlIGluIHRoZSBlbmQgdGhlIHJvd3MgZ2V0IHJlam9pbmVkIGFuZCB0aGVuIHJvdy1iaW5kZWQgZmFjdG9yIGdldHMgbWVzc2VkIHVwLl0KCmBgYHtyfQp0YURlYXRoREYgLT4gaG9sZCAjIGluIGNhc2Ugd2UgbWVzcyB1cAp0YURlYXRoREYgJT4lIGlubmVyX2pvaW4odm9sTmFtZVRhYmxlKSAlPiUgc2VsZWN0KHNjZW5hcmlvLCBldmVudEVudHJ5LCB2b2xOYW1lLCBldmVyeXRoaW5nKCkpIC0+IHRhRGVhdGhERgp0YURlYXRoREYKYGBgCgpXZSBjYW4gY2hlY2sgdGhpcyAtLSB3ZSBzaG91bGQgc2VlIGRpZmZlcmVudCB2b2x1bWUgbmFtZXMgYXNzb2NpYXRlZCB3aXRoIHZvbHVtZSBJRHMgZm9yIGRpZmZlcmVudCBzY2VuYXJpb3MKYGBge3J9CnRhRGVhdGhERiAlPiUgZGlzdGluY3Qoc2NlbmFyaW8sIHZvbHVtZVVJRCwgdm9sTmFtZSkgJT4lIGFycmFuZ2Uodm9sdW1lVUlEKQpgYGAKCkxldCdzIHBsb3Qgd2hlcmUgdGhpbmdzIGRpZQoKYGBge3J9CnRhRGVhdGhERiAlPiUgZ3JvdXBfYnkoc2NlbmFyaW8sIHZvbE5hbWUpICU+JSB0YWxseSgpICU+JSBhcnJhbmdlKHNjZW5hcmlvLCBkZXNjKG4pKSAtPiBkZWF0aFZvbHVtZVRhYmxlCmRlYXRoVm9sdW1lVGFibGUKYGBgCgpEaWQgYW55IG9mIHRoZW0gYWN0dWFsbHkgZGVjYXk/CmBgYHtyfQp0YURlYXRoREYgJT4lIGZpbHRlcih2b2xOYW1lID09ICJBcmNTZWN0aW9uWzAwXSIpCmBgYApVaCBvaCEhISEgSGFyZGx5IGFueSBkZWNheSEKCmBgYHtyfQp0YURlYXRoREYgJT4lIGdncGxvdCggYWVzKHggPSB2b2xOYW1lKSApICsgZ2VvbV9iYXIoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkKYGBgCgojIyBMb29rIGF0IHdoeSBoYXJkbHkgYW55IG11b25zIGRlY2F5LiAKCkxldCdzIGxvb2sgYXQgdGhlICdldmVyeXRoaW5nJyBzY2VuYXJpby4gCmBgYHtyfQpldmVyeXRoaW5nRmlsZSA8LSB3aGljaChzY2VuYXJpb3MgPT0gImV2ZXJ5dGhpbmciKQpgYGAKCkkgbWFkZSBhIG5ldyBUcmFja2luZ0FjdGlvbiByZWFkZXIKCmBgYHtyfQpyZWFkcjo6cmVhZF9maWxlKCd0cmFja2luZ0FjdGlvblJlYWRlckNoZWNrRGVjYXkucHknKSAlPiUgY2F0CmBgYApNYWtlIGFuIGluc3RhbmNlCmBgYHtyfQp0YURlY2F5Q2xhc3MgPC0gY3JlYXRlUmVhZGVyQ2xhc3NfZnJvbV9maWxlKCd0cmFja2luZ0FjdGlvblJlYWRlckNoZWNrRGVjYXkucHknKSRUcmFja2luZ0FjdGlvbldpdGhEZWNheVJlYWRlcgpgYGAKCkxvYWQgdGhlIGRhdGEgLSBsZXQncyBqdXN0IGxvb2sgYXQgb25lIGZpbGUgc2luY2UgaXQgdGFrZXMgYSBsb25nIHRpbWUKYGBge3J9CnRhUmVhZGVyIDwtIHRhRGVjYXlDbGFzcyhhcnRJbnB1dFRhZygiYXJ0ZzQiKSwgMjAwMDApCmdldEdhbGxlcnlEYXRhKGFyckZpbGVzW2V2ZXJ5dGhpbmdGaWxlXSwgdGFSZWFkZXIpCmBgYApgYGB7cn0KdGFkREYgPC0gZ2FsbGVyeVJlYWRlcl9kZih0YVJlYWRlcikKbnJvdyh0YWRERikKYGBgCk1ha2UgdGhpbmdzIGVhc2llciB0byBsb29rIGF0CmBgYHtyfQp0YWRERiAlPiUgCiAgbXV0YXRlKHIgICAgPSBzcXJ0KHgqeCt5KnkpLAogICAgICAgICBwbWFnID0gc3FydChweCpweCArIHB5KnB5ICsgcHoqcHopICkgJT4lIAogIHNlbGVjdCh0cmFja1R5cGUsIHZvbHVtZVVJRCwgc3RhdHVzLCByLCBlLCBwbWFnLCB0cmFja0lELCB0dXJuLCB4LCB5LCB6KSAtPiB0YWRERgpgYGAKClNvIEkgc2VlIGEgcG9zaXRyb24gaW4gYWxvbXN0IGV2ZXJ5IGNhc2UuIEJ1dCB0aGUgbXVvbiBhcHBlYXJzIHRvIHN0b3AgaW4gdGhlIGlyb24uIExldCdzIGp1c3QgcGxvdCB4LHoKCmBgYHtyfQp0YURlYXRoREYgJT4lIGdncGxvdChhZXMoeD16LCB5PXgpKSArIAogIGdlb21fcG9pbnQoKSArIHhsaW0oYygtMTAwMDAsIDEwMDAwKSkKYGBgCmBgYHtyfQp0bXAgPC0gb3BlbjNkKCkKYGBgCmBgYHtyfQpyaW5nIDwtIGN5bGluZGVyM2QoIGNlbnRlcj1yYmluZChjKDAsLTkwLCAwKSwgYygwLDkwLDApKSwKICAgICAgICAgICAgICAgICAgICByYWRpdXM9NzExMiwKICAgICAgICAgICAgICAgICAgICBzaWRlcz0yMCwgY2xvc2VkID0gRikKYGBgCmBgYHtyfQpjbGVhcjNkKCkKd2l0aCh0YURlYXRoREYsIAogICAgIHBsb3QzZCh4PXgsIHk9eSwgej16LCB0eXBlPSdwJywgbWFpbj0nTXVvbiBkZWF0aCBwb3NpdGlvbnMnLCAKICAgICAgIHhsYWI9IngiLCB5bGFiPSJ5IiwgemxhYj0ieiIsIHhsaW09YygtOTAwMCwgOTAwMCksIHpsaW09YygtOTAwMCwgOTAwMCkpKQpwbG90M2QocmluZywgYWRkPVQsIGFscGhhPTAuMikKdmlldzNkKHBoaT05MCwgdGhldGE9LTkwKQpyZ2x3aWRnZXQoKQpgYGAKVGhpcyByZWFsbHkgZG9lc24ndCBsb29rIGxpa2UgbXVvbnMgYXJlIGJlaW5nIHN0b3JlZC4KCldoYXQgZnJhY3Rpb24gb2YgbXVvbnMgaGF2ZSB6ZXJvIG1vbWVudHVtIHdoZW4gdGhleSBkaWUgKHNvIHRoZXkgZGllIGluIHNvbWV0aGluZyBkZW5zZSksCgpgYGB7cn0KdGFkREYgJT4lIAogIGZpbHRlciggdHJhY2tJRCA9PSAxLCBzdGF0dXMgPT0gMSkgJT4lIAogIHN1bW1hcml6ZShwWmVyb1BjdCA9IHN1bShwbWFnPDAuMSkvbnJvdyguKSoxMDApIC0+IHBaZXJvUGN0CnBaZXJvUGN0CmBgYApTbyBgciBwWmVyb1BjdFsxLDFdICU+JSBjYXRgJSBvZiBtdW9ucyBkaWUgaW4gc29tZXRoaW5nIGRlbnNlLiBIb3cgbWFueSBkZWNheT8KCmBgYHtyfQp0YWRERiAlPiUgZmlsdGVyKHN0YXR1cyA9PSAxLCB0cmFja0lEID09IDEsIHBtYWc+MC4xKSAgLT4gdGFkLm11b25zV2l0aFAuREYKdGFkLm11b25zV2l0aFAuREYKYGBgCgpgYGB7cn0KY2xlYXIzZCgpCndpdGgodGFkLm11b25zV2l0aFAuREYsIAogICAgIHBsb3QzZCh4PXgsIHk9eSwgej16LCB0eXBlPSdwJywgbWFpbj0nTXVvbiBkZWF0aCB3aXRoIG1vbWVudHVtIHBvc2l0aW9ucycsIAogICAgICAgeGxhYj0ieCIsIHlsYWI9InkiLCB6bGFiPSJ6IiwgeGxpbT1jKC05MDAwLCA5MDAwKSwgemxpbT1jKC05MDAwLCA5MDAwKSkpCnBsb3QzZChyaW5nLCBhZGQ9VCwgYWxwaGE9MC4yKQp2aWV3M2QocGhpPTkwLCB0aGV0YT0tOTApCnJnbHdpZGdldCgpCmBgYApUaGVzZSByZWFsbHkgZG9uJ3QgbG9vayBzdG9yZWQuCgojIyMgUmluZyB0cmFja2luZyBwbGFuZXMKCkxvb2sgYXQgdGhlIGBSaW5nVHJhY2tpbmdQbGFuZUFydFJlY29yZGAuCgpgYGByCnJlYWRlckNsYXNzU2tlbChleHRyYWN0Q2xhc3MgPSAiZ20ydHJ1dGg6OlJpbmdUcmFja2luZ1BsYW5lQXJ0UmVjb3JkIiwgZmlsbENsYXNzID0gImdtMnRydXRoOjpCYXNpY0FydFJlY29yZCIsIAogICAgICAgICAgICAgICAgd3JpdGVGaWxlID0gJ3JpbmdUcmFja2luZ1BsYW5lUmVhZGVyLnB5JykKYGBgCgpgYGB7cn0KcmVhZHI6OnJlYWRfZmlsZSgncmluZ1RyYWNraW5nUGxhbmVSZWFkZXIucHknKSAlPiUgY2F0CmBgYAoKYGBge3J9CnJ0cEMgPC0gY3JlYXRlUmVhZGVyQ2xhc3NfZnJvbV9maWxlKCdyaW5nVHJhY2tpbmdQbGFuZVJlYWRlci5weScpJFJpbmdUcmFja2luZ1BsYW5lQXJ0UmVjb3JkUmVhZGVyCmBgYAoKTWFrZSBvbmUgYW5kIGxvYWQgdGhlIGRhdGEKYGBge3J9CnJ0cFIgPC0gcnRwQyhhcnRJbnB1dFRhZygnYXJ0ZzQ6UmluZ1RyYWNraW5nUGxhbmVzJykpCmBgYAoKYGBge3J9CmdldEdhbGxlcnlEYXRhKGFyckZpbGVzW2V2ZXJ5dGhpbmdGaWxlXSwgcnRwUikgLT4gdGltaW5ncwpgYGAKSG93IG1hbnkgZXZlbnRzIGRpZCB3ZSBzZWUKYGBge3J9Cmxlbmd0aCh0aW1pbmdzJGV2ZW50VGltZXMpCmBgYAoKYGBge3J9CnJ0cERGIDwtIGdhbGxlcnlSZWFkZXJfZGYocnRwUikKbnJvdyhydHBERikKYGBgCk9ubHkgNCBldmVudHMgaGFkIGEgcmluZyB0cmFja2luZyBwbGFuZSBoaXQ/CgpgYGB7cn0KcnRwREYKYGBgCgojIyMgTmF0aGFuIGZpeGVzIGl0CgpOYXRoYW4gcHVzaGVkIHNvbWUgbm93IGNvZGUgdGhhdCBzdXBwb3NlZGx5IGZpeGVzIHRoaXMuIExldCdzIGxvb2suIAoKIyMjIyBSaW5nIHRyYWNraW5nIHBsYW5lcwoKYGBge3J9Cm15TmV3RmlsZSA8LSAnL2hvbWUvbWUvZ20yL3JlbmVlX2Fyci90cnkvbWRjMi0xL0FSUl91bmlmaWVkX2V2ZXJ5dGhpbmdfY3lsLnJvb3QnCmBgYAoKYGBge3J9CmdldEdhbGxlcnlEYXRhKG15TmV3RmlsZSwgcnRwUikgLT4gdGltaW5ncwpgYGAKCmBgYHtyfQpsZW5ndGgodGltaW5ncyRldmVudFRpbWVzKQpgYGAKCmBgYHtyfQpuZXdSdHBERiA8LSBnYWxsZXJ5UmVhZGVyX2RmKHJ0cFIpCm5yb3cobmV3UnRwREYpCmBgYAoKYGBge3J9Cm5ld1J0cERGCmBgYAoKTm93IEkgc2VlIGEgbG90IG1vcmUgbXVvbnMgZ29pbmcgYXJvdW5kIHRoZSByaW5nISEKCkhvdyBtYW55IHR1cm5zPwpgYGB7cn0KbmV3UnRwREYgJT4lIGdncGxvdChhZXMoeD1udHVybikpICsgZ2VvbV9oaXN0b2dyYW0oYmlucz0xMDApCmBgYApOb3Qgc3VyZSBpZiB0aGlzIGlzIHJpZ2h0LCBidXQgaXQgbG9va3MgYmV0dGVyIHRoYW4gYmVmb3JlIQoKTGV0J3MgbG9vayBhdCB0aGUgdm9sdW1lIG5hbWVzIGFuZCBzZWUgaG93IG1uYXkgb2YgdGhlc2UgbXVvbnMgZGVjYXkuIEl0IHNob3VsZCBiZSBhcm91bmQgMTAlIG9yIHNvLiAKCiMjIyMgVHJhY2tpbmcgYWN0aW9uIGFuZCB2b2x1bWUgaWRzCgpMZXQncyBsb2FkIHRoZSB0cmFja2luZyBhY3Rpb24gZGF0YSBhbmQgdGhlIHZvbHVtZSBuYW1lLiAKCkZpcnN0LCBsb2FkIHRoZSB0cmFja2luZyBhY3Rpb24gZGF0YS4uLgoKYGBge3J9CnRhblJlYWRlciA8LSB0cmFja2luZ0FjdGlvblJlYWRlckNsYXNzKGFydElucHV0VGFnKCJhcnRnNCIpLCAyMDAwMCkKZ2V0R2FsbGVyeURhdGEobXlOZXdGaWxlLCB0YW5SZWFkZXIpCmBgYAoKYGBge3J9CnRhbkRGIDwtIGdhbGxlcnlSZWFkZXJfZGYodGFuUmVhZGVyKQp0YW5ERgpgYGAKSnVzdCBsb29rIGF0IHRoZSBtdW9uIGRlYXRoCgpgYGB7cn0KdGFuREYgJT4lIGZpbHRlcih0cmFja0lEID09IDEsIHN0YXR1cyA9PSAxKSAtPiB0YW5EZWF0aERGCnRhbkRlYXRoREYKYGBgCgpQdXQgaW4gdm9sdW1lIG5hbWUKCmBgYHtyfQpydW5Wb2xTdHJpbmcgPC0gc3RyX2ludGVycCgiUFZTX0NTVk9VVD0ke2Nzdm91dH1fdm9sTmFtZXMuY3N2IGdtMiAtYyAke2ZjbFBhdGh9L2dtMmFuYWx5c2VzL2ZjbC9waHlzaWNhbFZvbHVtZVN0b3JlVG9GaWxlLmZjbCAtbiAxICR7YUZpbGV9IiwKICAgIGxpc3QoIGNzdm91dD0nbmF0aGFuRml4ZWQnLCBmY2xQYXRoPVN5cy5nZXRlbnYoIk1SQl9CVUlMRERJUiIpLCBhRmlsZT1teU5ld0ZpbGUpICkKcnVuVm9sU3RyaW5nCmBgYApgYGB7cn0Kc3lzdGVtKHJ1blZvbFN0cmluZykKYGBgCgpgYGB7cn0Kdm9sTmFtZXMgPC0gcmVhZF9jc3YoJ25hdGhhbkZpeGVkX3ZvbE5hbWVzLmNzdicsIGNvbF9uYW1lcz1jKCJ2b2x1bWVVSUQiLCAidm9sTmFtZSIpKQp2b2xOYW1lcwpgYGAKCkxldCdzIGpvaW4hCgpgYGB7cn0KdGFuRGVhdGhERiAlPiUgaW5uZXJfam9pbih2b2xOYW1lcykgJT4lIHNlbGVjdChldmVudEVudHJ5LCB2b2xOYW1lLCBldmVyeXRoaW5nKCkpIC0+IHRhbkRlYXRoREYKdGFuRGVhdGhERgpgYGAKYGBge3J9CnRhbkRlYXRoREYgJT4lIGRpc3RpbmN0KHZvbHVtZVVJRCwgdm9sTmFtZSkgJT4lIGFycmFuZ2Uodm9sdW1lVUlEKQpgYGAKYGBge3J9CnRhbkRlYXRoREYgJT4lIGdyb3VwX2J5KHZvbE5hbWUpICU+JSB0YWxseSgpICU+JSBhcnJhbmdlKGRlc2MobikpCmBgYApTbywgYWJvdXQgMSUgZGVjYXkhCgpgYGB7cn0KdGFuRGVhdGhERiAlPiUgCiAgbXV0YXRlKHIgICAgPSBzcXJ0KHgqeCt6KnopLAogICAgICAgICBwbWFnID0gc3FydChweCpweCArIHB5KnB5ICsgcHoqcHopICkgJT4lIAogIHNlbGVjdChldmVudEVudHJ5LCB0cmFja1R5cGUsIHZvbE5hbWUsIHN0YXR1cywgciwgcG1hZywgdHJhY2tJRCwgeCwgeSwgeikgLT4gdGFuRGVhdGhkREYKYGBgCgpgYGB7cn0KdGFuRGVhdGhkREYgJT4lIGZpbHRlcih2b2xOYW1lID09ICdBcmNTZWN0aW9uWzAwXScpCmBgYAoKRG8gdGhlc2UgY29ycmVzcG9uZCB0byB0aGUgbXVvbnMgdGhhdCBtYWtlIG1hbnkgdHVybnM/IFdlIG5lZWQgdG8gZG8gYSBqb2luIHdpdGggdGhlIHJpbmcgdHJhY2tpbmcgcGxhbmVzCgpgYGB7cn0KdGFuRGVhdGhkREYgJT4lIGlubmVyX2pvaW4obmV3UnRwREYsIGJ5PSJldmVudEVudHJ5Iiwgc3VmZml4PWMoIi50cmsiLCAicnRwIikpIC0+IHRhbkRlYXRoUlRQREYKdGFuRGVhdGhSVFBERgpgYGAKCkxldCdzIGxvb2sgYXQgZ3V5cyB0aGF0IGhhdmUgbW9yZSB0aGFuIG9uZSB0dXJuLiBSZW1lbWJlciwgd2UgZ2V0IGEgcm93IGZvciAqKmV2ZXJ5IGhpdCoqLiBJZiB3ZSByZWFsbHkgd2FudCB0byBzZWUgaG93IG1hbnkgdHVybnMgd2UgZ2V0LCB3ZSBuZWVkIHRvIGxvb2sgYXQgdGhlIGxhc3QgaGl0IGZvciB0aGUgZXZlbnQuIAoKYGBge3J9CnRhbkRlYXRoUlRQREYgJT4lIGZpbHRlcihmcmFjdHVybiA+PSAxKSAlPiUgYXJyYW5nZShldmVudEVudHJ5LCBmcmFjdHVybikgJT4lIGdyb3VwX2J5KGV2ZW50RW50cnkpICU+JSBkbyh0YWlsKC4sIDEpKSAlPiUKICBzZWxlY3QoZXZlbnRFbnRyeSwgdm9sTmFtZSwgciwgcG1hZywgZnJhY3R1cm4gKQpgYGAKCkhtbSAtIHdlJ3JlIG1pc3Npbmcgc29tZSBtdW9ucy4gTGV0J3MgbG9vayBhdCB0aG9zZS4KCmBgYHtyfQp0YW5EZWF0aFJUUERGICU+JSBmaWx0ZXIodm9sTmFtZSA9PSAnQXJjU2VjdGlvblswMF0nKSAlPiUgYXJyYW5nZShldmVudEVudHJ5LCBmcmFjdHVybikgJT4lIGdyb3VwX2J5KGV2ZW50RW50cnkpICU+JSBkbyh0YWlsKC4sIDEpKSAlPiUKICBzZWxlY3QoZXZlbnRFbnRyeSwgdm9sTmFtZSwgciwgcG1hZywgZnJhY3R1cm4gKQoKYGBgCgpXaGF0IGhhcHBlbmVkIHRvIGV2ZW50IDc5MT8KCmBgYHtyfQpuZXdSdHBERiAlPiUgZmlsdGVyKGV2ZW50RW50cnkgPT0gNzkxKQpgYGAKCkxldCdzIGxvb2sgYXQgc29tZSBtdW9uIHRyYWNrcy4gSG93IGFib3V0IDc5MSBhbmQgODkzLiBTaW5jZSBvdXIgZmlsZSBpcyBvbiBkaXNrLCB0aGlzIHdpbGwgYmUgZmFzdGVyLiAKCgoK